"""
This patch implement a coercion map for tensor.
"""
from sage.combinat.cha.patches.monkey_patching import MonkeyPatch

from sage.combinat.free_module import CombinatorialFreeModule_Tensor

from sage.categories.modules_with_basis import ModulesWithBasis
from sage.misc.misc import forall


class _(MonkeyPatch, CombinatorialFreeModule_Tensor):

        def _coerce_map_from_(self, TensorParent):
            r"""
            EXAMPLES::

                sage: C = CombinatorialFreeModule(ZZ, Set([1,2]))
                sage: D = CombinatorialFreeModule(ZZ, Set([2,4]))
                sage: f = C.module_morphism(
                ....:    on_basis=lambda x: D.monomial(2*x),
                ....:    codomain=D)
                sage: f.register_as_coercion()
                sage: T = tensor((C,C))
                sage: p = D.an_element()
                sage: T(tensor((p,p)))
                Traceback (most recent call last):
                ...
                AssertionError: do not know how to coerce Free module\
                generated by {2, 4} over Integer Ring # Free module generated\
                by {2, 4} over Integer Ring into Free module generated by\
                {1, 2} over Integer Ring # Free module generated by {1, 2}\
                over Integer Ring (there is no coercion map from Free module\
                generated by {2, 4} over Integer Ring into Free module\
                generated by {1, 2} over Integer Ring)
                sage: T = tensor((D,D))
                sage: p = C.an_element()
                sage: T(tensor((p,p)))
                4*B[2] # B[2] + 4*B[2] # B[4] + 4*B[4] # B[2] + 4*B[4] # B[4]
            """
            assert(isinstance(TensorParent, CombinatorialFreeModule_Tensor))
            modules = TensorParent._sets
            assert(forall(
                modules,
                lambda module: module in ModulesWithBasis(self.base_ring())
                ))
            for i, Parent_i in enumerate(modules):
                assert (self._sets[i]).has_coerce_map_from(Parent_i), \
                ("do not know how to coerce %s into %s (there is no coercion" +
                " map from %s into %s)") % (
                    TensorParent, self, Parent_i, self._sets[i])
            vector_map = [(self._sets[i]).coerce_map_from(module)
                          for i, module in enumerate(modules)]
            result = TensorParent.module_morphism(
                    on_basis=lambda x: self._tensor_of_elements([
                            vector_map[i](modules[i].monomial(x[i]))
                            for i in range(len(modules))
                    ]), codomain=self)
            return result
